package cn.zf233.xcloud.service.impl;

import cn.zf233.xcloud.commom.Const;
import cn.zf233.xcloud.commom.R;
import cn.zf233.xcloud.commom.enums.ClassifyENUM;
import cn.zf233.xcloud.commom.enums.SortFieldENUM;
import cn.zf233.xcloud.config.lockpool.IXCloudThreadPool;
import cn.zf233.xcloud.entity.File;
import cn.zf233.xcloud.entity.Share;
import cn.zf233.xcloud.entity.User;
import cn.zf233.xcloud.mapper.FileMapper;
import cn.zf233.xcloud.mapper.ShareMapper;
import cn.zf233.xcloud.mapper.UserMapper;
import cn.zf233.xcloud.service.AdminService;
import cn.zf233.xcloud.service.FileService;
import cn.zf233.xcloud.service.SearchService;
import cn.zf233.xcloud.service.ShareService;
import cn.zf233.xcloud.service.core.PersistenceService;
import cn.zf233.xcloud.util.CommonUtil;
import cn.zf233.xcloud.util.OSSUtil;
import cn.zf233.xcloud.util.ThreadSafeDateUtil;
import cn.zf233.xcloud.vo.AbsolutePath;
import cn.zf233.xcloud.vo.FileVo;
import com.aliyun.oss.OSS;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Created by zf233 on 2020/12/25
 * 文件服务
 */
@Service
public class FileServiceImpl implements FileService, PersistenceService.IFailFileRemove {

    private final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);

    @Resource
    private FileMapper fileMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private ShareMapper shareMapper;

    @Resource
    private SearchService searchService;

    @Resource
    private ShareService shareService;

    @Resource
    private InnerFileService innerFileService;

    @Resource
    private OSSUtil ossUtil;

    @Resource
    private IXCloudThreadPool<Lock> lockPool;

    @Resource
    private FileServiceCommon fileServiceCommon;

    @Override
    public R<List<FileVo>> list(User user,
                                Long parentId,
                                Integer sortFlag,
                                Integer sortType) {
        if (parentId == null || parentId == -1) { // 根目录
            File rootNode = fileMapper.selectRootNodeOfUserByPrimaryKey(user.getId());
            parentId = rootNode.getId();
        }
        File fileInfo = fileMapper.selectByPrimaryKey(parentId);
        if (fileInfo == null || fileInfo.isDeleted()) {
            return R.createByErrorIllegalArgument("目标文件夹不存在");
        }
        List<File> fileList;
        if (sortFlag != null && sortType != null) { // 排序
            if (sortType == 1) { // 升序
                fileList = fileMapper.selectFilesByUserIDAndParentIDSortByTypeDesc(user.getId(), parentId, SortFieldENUM.fieldOf(sortFlag).getDescribe());
            } else { // 降序
                fileList = fileMapper.selectFilesByUserIDAndParentIDSortByTypeAsce(user.getId(), parentId, SortFieldENUM.fieldOf(sortFlag).getDescribe());
            }
        } else { // 不做任何操作
            fileList = fileMapper.selectFilesByUserIDAndParentID(user.getId(), parentId);
        }
        List<AbsolutePath> absolutePathList = getAbsolutePath(user.getId(), parentId);

        // 包装并返回
        return R.createBySuccessAbsolutePathMsg("文件列表获取成功", CommonUtil.assembleFileVoDetail(fileList), absolutePathList);
    }

    @Override
    public R<List<FileVo>> search(User user, String matchCode) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登录");
        }
        List<File> fileList;
        try {
            fileList = searchService.search(userInfo, matchCode);
        } catch (Exception e) {
            this.logger.error(" ****ES INDEX CONTROL**** EXCEPTION SEARCH  -------------> " + e.getMessage());
            fileList = this.fileMapper.selectFilesByUserIDAndMatchCode(user.getId(), matchCode);
        }
        if (!fileList.isEmpty()) {
            return R.createBySuccess("搜索成功", CommonUtil.assembleFileVoDetail(fileList));
        }
        return R.createBySuccessMessage("未找到任何文件");
    }

    @Override
    public R<List<FileVo>> listRecycle(User user) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登录");
        }
        List<File> recycle = fileMapper.selectFilesByUserIDAndDeleteFlag(userInfo.getId());
        return R.createBySuccess("废纸篓列表获取成功", CommonUtil.assembleFileVoDetail(recycle));
    }

    @Override
    public R<List<FileVo>> classify(User user, Integer classifyType) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登录");
        }
        if (!ClassifyENUM.exists(classifyType)) {
            return R.createByErrorIllegalArgument("指定分类参数有误");
        }
        List<File> fileList;
        try {
            fileList = searchService.classify(user, classifyType);
        } catch (Exception e) {
            this.logger.error(" ****SEARCH SERVICE**** THROW EXCEPTION CLASSIFY --------> " + e.getMessage());
            fileList = fileMapper.selectFilesByUserIDAndClassify(userInfo.getId(), classifyType);
        }
        return R.createBySuccess("分类列表获取成功", CommonUtil.assembleFileVoDetail(fileList));
    }

    // 保存文件信息到数据库（锁装饰）
    @Override
    public R save(User user, List<File> fileList, Long parentId) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }

        Lock lock = lockPool.getCapacityLock(userInfo.getId());
        lock.lock();
        try {
            return innerFileService.save(userInfo.getId(), fileList, parentId);
        } finally {
            lockPool.unLock(lock);
        }
    }

    // 下载文件-获取下载地址（锁装饰）
    @Override
    public R download(User user, Long fileId) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }

        Lock lock = lockPool.getDownloadLock(userInfo.getId());
        lock.lock();
        try {
            return innerFileService.download(userInfo.getId(), fileId);
        } finally {
            lockPool.unLock(lock);
        }
    }

    @Override
    public R rename(User user, Long fileId, String newFileName) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }

        Lock lock = lockPool.getFileRenameLock(userInfo.getId());
        try {
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                return innerFileService.rename(fileId, newFileName);
            }
        } catch (InterruptedException e) {
            this.logger.error(" ****RENAME FILE LOCK**** EXCEPTION INTERRUPTED  --------> " + e.getMessage());
        } finally {
            lockPool.unLock(lock);
        }
        return R.createByErrorMessage("服务器繁忙，请稍后再试");
    }

    @Override
    @Transactional
    public R recycle(Long[] fileId, User user) {
        if (fileId == null || fileId.length == 0) {
            return R.createByErrorMessage("没有要删除的文件");
        }
        if (userMapper.selectByPrimaryKey(user.getId()) == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }
        File rootNode = fileMapper.selectRootNodeOfUserByPrimaryKey(user.getId());
        if (rootNode == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }
        List<Long> shareIdList = new LinkedList<>();
        int count = 0;
        for (long id : fileId) {
            File fileInfo = fileMapper.selectByPrimaryKey(id);
            if (fileInfo == null) {
                continue;
            }
            fileInfo.setDeleteFlag(1);
            fileInfo.setParentId(rootNode.getId());
            if (fileInfo.isAFolder()) {
                List<File> fileList = new LinkedList<>();
                fileServiceCommon.findChildByParentId(fileList, fileInfo.getId(), user.getId());
                shareIdList.addAll(fileList
                        .stream()
                        .filter(file -> !file.isAFolder() && file.getShareId() != null && file.getShareId() != -1)
                        .map(File::getShareId)
                        .collect(Collectors.toList()));
                for (File file : fileList) {
                    file.setDeleteFlag(1);
                    file.setParentId(rootNode.getId());
                    if (!file.isAFolder()) file.setShareId(-1L);
                    int result = fileMapper.updateByPrimaryKeySelective(file);
                    if (result > 0) {
                        try {
                            searchService.insertOrUpdateOne(file);
                        } catch (Exception e) {
                            this.logger.error(" ****SEARCH SERVICE**** THROW EXCEPTION INSERT ----------> " + e.getMessage());
                        }
                        count++;
                    }
                }
            } else {
                if (fileInfo.getShareId() != null && fileInfo.getShareId() != -1)
                    shareIdList.add(fileInfo.getShareId());
                fileInfo.setShareId(-1L);
                int result = fileMapper.updateByPrimaryKeySelective(fileInfo);
                if (result > 0) {
                    try {
                        searchService.insertOrUpdateOne(fileInfo);
                    } catch (Exception e) {
                        this.logger.error(" ****SEARCH SERVICE**** THROW EXCEPTION INSERT ----------> " + e.getMessage());
                    }
                    count++;
                }
            }
        }
        shareService.remove(shareIdList.toArray(new Long[0]));
        return R.createBySuccessMessage(count + "个文件/文件夹已放至废纸篓");
    }

    @Override
    @Transactional
    public R restore(Long[] fileId, User user) {
        if (fileId == null || fileId.length == 0) {
            return R.createByErrorMessage("没有要放回的文件");
        }
        if (userMapper.selectByPrimaryKey(user.getId()) == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }
        int count = 0;
        for (long id : fileId) {
            File fileInfo = fileMapper.selectByPrimaryKey(id);
            if (fileInfo == null) {
                continue;
            }
            fileInfo.setDeleteFlag(0);
            int result = fileMapper.updateByPrimaryKeySelective(fileInfo);
            if (result > 0) {
                try {
                    searchService.insertOrUpdateOne(fileInfo);
                } catch (Exception e) {
                    this.logger.error(" ****SEARCH SERVICE**** THROW EXCEPTION INSERT ----------> " + e.getMessage());
                }
                count++;
            }
        }
        return R.createBySuccessMessage(count + "个文件/文件夹已放回");
    }

    @Override
    public R flushRecycle(User user) {
        if (userMapper.selectByPrimaryKey(user.getId()) == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }
        List<File> fileList = fileMapper.selectFilesByUserIDAndDeleteFlag(user.getId());
        if (fileList == null || fileList.isEmpty()) {
            return R.createBySuccessMessage("废纸篓已经很干净了哦");
        }
        Long[] fileIdArray = fileList.stream().map(File::getId).toArray(Long[]::new);
        R response = remove(fileIdArray, user);
        if (response.isSuccess()) {
            return R.createBySuccessMessage("清倒成功，" + response.getMsg().replace("删除", ""));
        }
        return response;
    }

    @Override
    public R flushTimeoutRecycle() {
        final long timout = TimeUnit.DAYS.toMillis(30L);
        final long now = ThreadSafeDateUtil.now();
        List<User> userList = userMapper.selectUsers();
        if (userList == null || userList.isEmpty()) {
            return R.createByErrorMessage("无用户");
        }
        List<File> ossDeleteBatchList = new ArrayList<>();
        for (User user : userList) {
            List<File> fileList = fileMapper.selectFilesByUserIDAndDeleteFlag(user.getId());
            if (fileList == null || fileList.isEmpty()) {
                continue;
            }
            for (File file : fileList) {
                if (now - file.getUpdateTime() < timout) {
                    continue;
                }
                int result = fileMapper.deleteByPrimaryKey(file.getId());
                if (result <= 0 || file.isAFolder()) {
                    continue;
                }
                ossDeleteBatchList.add(file);
            }
        }
        fileServiceCommon.removeOSSFileBatch(ossDeleteBatchList);
        return R.createByErrorMessage("所有用户废纸篓超时文件移除成功");
    }

    // 删除单个文件、文件夹或递归删除整个文件夹（锁装饰）
    @Override
    public R remove(Long[] fileIdArray, User user) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }

        Lock lock = lockPool.getCapacityLock(userInfo.getId());
        try {
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                return innerFileService.remove(userInfo.getId(), fileIdArray);
            }
        } catch (InterruptedException e) {
            this.logger.error(" ****REMOVE FILE LOCK**** EXCEPTION INTERRUPTED  --------> " + e.getMessage());
        } finally {
            lockPool.unLock(lock);
        }
        return R.createByErrorMessage("服务器繁忙，请稍后再试");
    }

    @Override
    public R move(User user, Long[] fileIdArray, Long parentId) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }

        Lock lock = lockPool.getMoveOrCopyLock(userInfo.getId());
        try {
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                return innerFileService.move(userInfo.getId(), fileIdArray, parentId);
            }
        } catch (InterruptedException e) {
            this.logger.error(" *****MOVE FILE LOCK***** EXCEPTION INTERRUPTED  --------> " + e.getMessage());
        } finally {
            lockPool.unLock(lock);
        }
        return R.createByErrorMessage("服务器繁忙，请稍后再试");
    }

    @Override
    public R copy(User user, Long[] fileIdArray, Long parentId) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }

        Lock lock = lockPool.getMoveOrCopyLock(userInfo.getId());
        try {
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                return innerFileService.copy(userInfo.getId(), fileIdArray, parentId);
            }
        } catch (InterruptedException e) {
            this.logger.error(" *****COPY FILE LOCK***** EXCEPTION INTERRUPTED  --------> " + e.getMessage());
        } finally {
            lockPool.unLock(lock);
        }
        return R.createByErrorMessage("服务器繁忙，请稍后再试");
    }

    // 创建文件夹
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R newFolder(User user, String folderName, Long parentId) {
        if (StringUtils.isBlank(folderName) || folderName.length() > 100) {
            return R.createByErrorMessage("文件夹名称格式有误，不可超过100个字符");
        }
        File file = new File();
        if (parentId == null || parentId == -1) {
            File rootNode = fileMapper.selectRootNodeOfUserByPrimaryKey(user.getId());
            file.setParentId(rootNode.getId());
        } else {
            File fileOfParentId = fileMapper.selectByPrimaryKey(parentId);
            if (fileOfParentId == null || fileOfParentId.isDeleted()) {
                return R.createByErrorIllegalArgument("目标文件夹不存在");
            }
            file.setParentId(parentId);
        }

        file.setShareId(-1L);
        file.setOldFileName(folderName);
        file.setUserId(user.getId());
        file.setFolder(1);
        file.setClassify(0);
        file.setDeleteFlag(0);

        int result = fileMapper.insert(file);
        if (result > 0) {
            try {
                searchService.insertOrUpdateOne(file);
            } catch (Exception e) {
                this.logger.error(" ****SEARCH SERVICE**** THROW EXCEPTION INSERT ----------> " + e.getMessage());
            }
            return R.createBySuccessMessage("创建成功");
        }
        return R.createBySuccessMessage("创建失败");
    }

    @Override
    public R getDetail(Long fileId) {
        File fileInfo = fileMapper.selectByPrimaryKey(fileId);
        if (fileInfo != null) {
            return R.createBySuccess("获取文件详情成功", CommonUtil.assembleFileVoDetail(fileInfo));
        }
        return R.createByErrorIllegalArgument("文件详情获取失败");
    }

    @Override
    public R linkArchived(User user, Long parentId, String url, Integer type) {
        if (StringUtils.isBlank(url) || !url.contains(Const.FILE_SHARE_LINK_PREFIX)) {
            return R.createByErrorIllegalArgument("分享链接链接解析有误");
        }

        url = url.replace(Const.FILE_SHARE_LINK_PREFIX, "");
        String[] split = url.split("/");
        int userId = Integer.parseInt(split[0]);
        long fileId = Integer.parseInt(split[1]);
        String code = split[3];
        long now = ThreadSafeDateUtil.now();

        File fileInfo = fileMapper.selectByPrimaryKey(fileId);
        boolean existed = fileInfo != null
                && fileInfo.getUserId() == userId
                && fileInfo.getShareId() != null
                && fileInfo.getShareId() != -1;

        if (existed) {
            Share shareInfo = shareMapper.selectByPrimaryKey(fileInfo.getShareId());
            if (shareInfo == null) {
                return R.createByErrorMessage("此文件未分享");
            }
            boolean flag;
            if (type == 0) {
                if (StringUtils.isBlank(shareInfo.getQrLink())) {
                    return R.createByErrorMessage("分享二维码解析有误");
                }
                if (now - shareInfo.getQrLinkTime() > shareInfo.getQrLinkTimeout()) {
                    return R.createByErrorMessage("分享二维码已超时");
                }
                flag = shareInfo.getQrLinkCheckCode().equals(code);
            } else {
                if (StringUtils.isBlank(shareInfo.getShareLink())) {
                    return R.createByErrorMessage("分享链接解析有误");
                }
                if (now - shareInfo.getShareLinkTime() > shareInfo.getShareLinkTimeout()) {
                    return R.createByErrorMessage("分享链接已超时");
                }
                flag = shareInfo.getShareLinkCheckCode().equals(code);
            }
            if (flag) {
                Long[] fileIdArray = new Long[]{fileId};
                R rCopy = copy(user, fileIdArray, parentId);
                if (rCopy.isSuccess()) {
                    return R.createBySuccessMessage("文件转存成功");
                }
                return R.createByErrorMessage("文件转存失败");
            }
        }
        return R.createByErrorMessage("参数有误或已失效，转存失败");
    }

    @Override
    public R listSubFolder(User user, Long parentId) {
        User userInfo = userMapper.selectByPrimaryKey(user.getId());
        if (userInfo == null) {
            return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
        }
        if (parentId == null || parentId == -1) {
            File fileInfo = fileMapper.selectRootNodeOfUserByPrimaryKey(user.getId());
            if (fileInfo == null) {
                return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
            }
            parentId = fileInfo.getId();
        } else {
            File fileInfo = fileMapper.selectByPrimaryKey(parentId);
            if (fileInfo == null) {
                return R.createByErrorIllegalArgument("不存在的文件夹");
            }
        }

        Map<String, Object> map = new HashMap<>(4);
        List<File> fileList = fileMapper.selectFilesByUserIDAndParentID(user.getId(), parentId);
        List<FileVo> fileVoList = CommonUtil.assembleFileVoDetail(fileList);
        List<FileVo> folderList = fileVoList.stream().filter(FileVo::isAFolder).collect(Collectors.toList());

        map.put("parentId", parentId);
        map.put("folderVo", folderList);
        map.put("absolutePath", getAbsolutePath(user.getId(), parentId));
        return R.createBySuccess("获取成功", map);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeByRandomName(String randomFileName) {
        if (StringUtils.isBlank(randomFileName)) {
            return;
        }
        File fileInfo = fileMapper.selectByRandomFileName(randomFileName);
        if (fileInfo == null) {
            return;
        }
        int result = fileMapper.deleteByPrimaryKey(fileInfo.getId());
        if (result <= 0) {
            return;
        }
        if (null != fileInfo.getShareId() && fileInfo.getShareId() != -1) {
            shareService.remove(new Long[]{fileInfo.getShareId()});
        }
        OSS ossClient = ossUtil.getOSSClient();
        if (ossUtil.objectNameExists(ossClient, randomFileName)) {
            ossUtil.delete(ossClient, randomFileName);
        }
        ossClient.shutdown();
    }

    // 递归查询parentId所在绝对路径
    @Override
    public List<AbsolutePath> getAbsolutePath(Long userId, Long parentId) {
        File fileOfParentId = fileMapper.selectByPrimaryKey(parentId);
        if (fileOfParentId == null) {
            File rootNode = fileMapper.selectRootNodeOfUserByPrimaryKey(userId);
            parentId = rootNode.getId();
        }
        List<AbsolutePath> absolutePathList = new ArrayList<>();
        fileServiceCommon.findAbsolutePathByParentId(absolutePathList, parentId);
        Collections.reverse(absolutePathList);
        return absolutePathList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeWildFile() {
        OSS ossClient = ossUtil.getOSSClient();
        List<Long> wildFileIds = fileMapper.getWildFiles();
        for (Long id : wildFileIds) {
            File file = fileMapper.selectByPrimaryKey(id);
            if (file == null) {
                continue;
            }
            if (!file.isAFolder()) {
                if (file.getShareId() != null && file.getShareId() != -1) {
                    shareService.remove(new Long[]{file.getShareId()});
                }
                if (ossUtil.objectNameExists(ossClient, file.getRandomFileName())) {
                    ossUtil.delete(ossClient, file.getRandomFileName());
                }
            }
            fileMapper.deleteByPrimaryKey(id);
        }
        ossClient.shutdown();
    }

    @Override
    public void remove(String fileName) {
        this.removeByRandomName(fileName);
    }

    @Component
    public static class InnerFileService {

        private final Logger logger = LoggerFactory.getLogger(InnerFileService.class);
        private final UserMapper userMapper;
        private final FileMapper fileMapper;
        private final SearchService searchService;
        private final AdminService adminService;
        private final FileServiceCommon fileServiceCommon;
        private final OSSUtil ossUtil;

        @Autowired
        public InnerFileService(UserMapper userMapper, FileMapper fileMapper, SearchService searchService,
                                AdminService adminService, FileServiceCommon fileServiceCommon, OSSUtil ossUtil) {
            this.userMapper = userMapper;
            this.fileMapper = fileMapper;
            this.searchService = searchService;
            this.adminService = adminService;
            this.fileServiceCommon = fileServiceCommon;
            this.ossUtil = ossUtil;
        }

        @Transactional(rollbackFor = Exception.class)
        public R save(Long userId, List<File> fileList, Long parentId) {
            User user = userMapper.selectByPrimaryKey(userId);
            if (user == null) {
                return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
            }
            if (fileList == null || fileList.isEmpty()) {
                return R.createByErrorMessage("上传列表为空");
            }
            if (parentId == null || parentId == -1) {
                File rootNode = fileMapper.selectRootNodeOfUserByPrimaryKey(user.getId());
                parentId = rootNode.getId();
            }
            File target = fileMapper.selectByPrimaryKey(parentId);
            if (target == null || target.isDeleted()) {
                fileServiceCommon.removeOSSFileBatch(fileList);
                return R.createByErrorIllegalArgument("目标文件夹不存在");
            }

            Long finalParentId = parentId;
            long uploadFileSizeTotal = fileList
                    .stream()
                    .peek(file -> file.setParentId(finalParentId))
                    .mapToLong(File::getFileSize)
                    .sum();
            if (user.getUseCapacity() + uploadFileSizeTotal > user.getCapacity()) {
                fileServiceCommon.removeOSSFileBatch(fileList);
                return R.createByErrorMessage("上传失败(空间已满)");
            }

            List<File> failFileList = new ArrayList<>();
            for (File file : fileList) {
                int result = fileMapper.insert(file);
                if (result > 0) {
                    try {
                        searchService.insertOrUpdateOne(file);
                    } catch (Exception e) {
                        logger.error(" ****SEARCH SERVICE**** THROW EXCEPTION INSERT ----------> " + e.getMessage());
                    }
                } else {
                    failFileList.add(file);
                    uploadFileSizeTotal -= file.getFileSize();
                }
            }

            if (!failFileList.isEmpty()) {
                fileServiceCommon.removeOSSFileBatch(failFileList);
            }

            user.setUseCapacity(user.getUseCapacity() + uploadFileSizeTotal);
            int resultUser = userMapper.updateByPrimaryKeySelective(user);
            if (resultUser > 0) {
                return R.createBySuccessMessage("上传成功");
            }

            fileServiceCommon.removeOSSFileBatch(fileList);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return R.createByErrorMessage("上传失败");
        }

        @Transactional(rollbackFor = Exception.class)
        public R download(Long userId, Long fileId) {
            User user = userMapper.selectByPrimaryKey(userId);
            if (user == null) {
                return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
            }
            long flow = user.getDownloadFlow();
            if (flow <= 0) {
                return R.createByErrorMessage("用户下载余量已用尽，次日刷新");
            }
            File fileInfo = fileMapper.selectByPrimaryKey(fileId);
            if (fileInfo != null && !fileInfo.isAFolder()) {
                adminService.downloadFlow(fileInfo.getFileSize());
                if (!user.getId().equals(fileInfo.getUserId())) {
                    return R.createByErrorMessage("没有权限获取此文件");
                }
                if (fileInfo.isDeleted()) {
                    return R.createByErrorMessage("废纸篓内文件不支持下载");
                }
                if (flow >= fileInfo.getFileSize()) {
                    user.setDownloadFlow(flow - fileInfo.getFileSize());
                } else {
                    return R.createByErrorMessage("用户下载余量已用尽，次日刷新");
                }
                int result = userMapper.updateByPrimaryKeySelective(user);
                if (result > 0) {
                    return R.createBySuccess("获取下载链接成功", Const.OSS_PATH_PREFIX + fileInfo.getRandomFileName());
                }
                return R.createByErrorMessage("获取下载链接失败");
            }
            return R.createByErrorMessage("无效的目标文件");

        }

        @Transactional(rollbackFor = Exception.class)
        public R rename(Long fileId, String newFileName) {
            File fileInfo = fileMapper.selectByPrimaryKey(fileId);
            if (fileInfo == null) {
                return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
            }
            if (fileInfo.isAFolder()) {
                if (StringUtils.isBlank(newFileName)) {
                    return R.createByErrorMessage("文件夹命名不规范");
                }
                if (newFileName.length() > 32) {
                    return R.createByErrorMessage("文件夹名称不可超过32个字符");
                }
            } else {
                if (newFileName.contains(".")) {
                    newFileName = newFileName.substring(0, newFileName.lastIndexOf("."));
                }
                newFileName = newFileName + fileInfo.getOldFileName().substring(fileInfo.getOldFileName().lastIndexOf("."));
                final String regex = "[^\\u4E00-\\u9FA5\\uF900-\\uFA2D\\w-_.,()（）《》]";
                Pattern pattern = Pattern.compile(regex);
                Matcher matcher = pattern.matcher(newFileName);
                if (!(!matcher.find() && newFileName.contains("."))) {
                    return R.createByErrorMessage("文件命名不规范");
                }
            }

            fileInfo.setOldFileName(newFileName);
            int result = fileMapper.updateByPrimaryKeySelective(fileInfo);
            if (result > 0) {
                if (!fileInfo.isAFolder()) {
                    OSS ossClient = ossUtil.getOSSClient();
                    if (ossUtil.objectNameExists(ossClient, fileInfo.getRandomFileName())) {
                        ossUtil.rename(ossClient, fileInfo.getRandomFileName(), newFileName, fileInfo.getFileSize());
                    }
                    ossClient.shutdown();
                }
                try {
                    searchService.insertOrUpdateOne(fileInfo);
                } catch (Exception e) {
                    logger.error(" ****SEARCH SERVICE**** THROW EXCEPTION INSERT ----------> " + e.getMessage());
                }
                return R.createBySuccessMessage("修改成功，关闭窗口以展示");
            }
            return R.createByErrorMessage("修改失败");
        }

        @Transactional(rollbackFor = Exception.class)
        public R remove(Long userId, Long[] fileIdArray) {
            User user = userMapper.selectByPrimaryKey(userId);
            if (user == null) {
                return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
            }
            int count = 0;
            long removeFilesSizeTotal = 0L;
            List<File> removeFileList = new ArrayList<>();
            for (Long fileId : fileIdArray) {
                File targetFile = fileMapper.selectByPrimaryKey(fileId);
                if (targetFile == null) {
                    continue;
                }
                List<File> fileLinkedList = new LinkedList<>();
                fileServiceCommon.findChildByParentId(fileLinkedList, fileId, user.getId());
                for (File file : fileLinkedList) {
                    int result = fileMapper.deleteByPrimaryKey(file.getId());
                    if (result <= 0) {
                        continue;
                    }
                    count++;
                    if (!file.isAFolder()) {
                        removeFilesSizeTotal += file.getFileSize();
                        removeFileList.add(file);
                    }
                    try {
                        searchService.deleteOne(file);
                    } catch (Exception e) {
                        logger.error(" ****SEARCH SERVICE**** THROW EXCEPTION DELETE ----------> " + e.getMessage());
                    }
                }
            }

            user.setUseCapacity(user.getUseCapacity() - removeFilesSizeTotal);
            int result = userMapper.updateByPrimaryKeySelective(user);
            if (result > 0) {
                fileServiceCommon.removeOSSFileBatch(removeFileList);
                return R.createBySuccessMessage("共计删除" + count + "个文件或文件夹");
            }

            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return R.createByErrorMessage("删除失败");
        }

        @Transactional(rollbackFor = Exception.class)
        public R move(Long userId, Long[] fileIdArray, Long parentId) {
            // 验证参数
            User user = userMapper.selectByPrimaryKey(userId);
            if (user == null) {
                return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
            }
            if (fileIdArray == null || fileIdArray.length <= 0) {
                return R.createByErrorMessage("未选中要移动的文件");
            }
            if (parentId == null) {
                return R.createByErrorMessage("未指定目标文件夹");
            }
            File targetFolder = fileMapper.selectByPrimaryKey(parentId);
            if (targetFolder == null || targetFolder.isDeleted()) {
                return R.createByErrorIllegalArgument("目标文件夹不存在");
            }

            // 获取目标文件夹的绝对路径
            List<AbsolutePath> absolutePathList = new ArrayList<>();
            fileServiceCommon.findAbsolutePathByParentId(absolutePathList, parentId);
            Set<Long> absoluteIdSet = absolutePathList
                    .stream()
                    .map(AbsolutePath::getParentid)
                    .collect(Collectors.toSet());

            // 开始复制
            for (Long fileId : fileIdArray) {
                File fileInfo = fileMapper.selectByPrimaryKey(fileId);

                // 验证参数
                if (fileInfo == null || fileInfo.isDeleted()) {
                    continue;
                }
                if (fileInfo.getParentId().equals(parentId)) {
                    return R.createByErrorMessage("已在此文件夹中");
                }

                // 移动
                fileInfo.setParentId(parentId);
                if (fileInfo.isAFolder()) {

                    // 防止父级文件夹移动至子级文件夹造成文件目录层级关系丢失
                    if (!absoluteIdSet.contains(fileInfo.getId())) {
                        fileMapper.updateByPrimaryKeySelective(fileInfo);
                    }
                } else {
                    fileMapper.updateByPrimaryKeySelective(fileInfo);
                }
            }
            return R.createBySuccessMessage("移动成功");
        }


        @Transactional(rollbackFor = Exception.class)
        public R copy(Long userId, Long[] fileIdArray, Long parentId) {

            // 验证参数
            User user = userMapper.selectByPrimaryKey(userId);
            if (user == null) {
                return R.createByErrorIllegalArgument("登陆失效，请重新登陆");
            }
            if (fileIdArray == null || fileIdArray.length <= 0) {
                return R.createByErrorMessage("未选中要复制的文件");
            }
            if (parentId == null) {
                return R.createByErrorMessage("未指定目标文件夹");
            }
            File targetFolder = fileMapper.selectByPrimaryKey(parentId);
            if (targetFolder == null || targetFolder.isDeleted()) {
                return R.createByErrorIllegalArgument("目标文件夹不存在");
            }

            // 判断容量
            final long id = user.getId();
            long sizeCount = Arrays
                    .stream(fileIdArray)
                    .mapToLong(fileId -> fileServiceCommon.getParentIdSizeCount(fileId, id))
                    .sum();
            if (user.getCapacity() - user.getUseCapacity() < sizeCount) {
                return R.createByErrorMessage("容量不足");
            }

            // 创建一个临时文件夹
            File temporary = new File();
            temporary.setOldFileName("copy_temporary_folder");
            temporary.setFolder(1);
            temporary.setClassify(-1);
            if (fileMapper.insert(temporary) <= 0) {
                return R.createByErrorMessage("复制失败，请重试");
            }

            // 开始复制
            OSS ossClient = ossUtil.getOSSClient();
            List<File> copyFileList = new LinkedList<>(); // 记录复制成功的文件
            long sum = Arrays
                    .stream(fileIdArray)
                    .mapToLong(fileId -> copy(fileId, temporary.getId(), id, ossClient, copyFileList))
                    .sum();
            ossClient.shutdown();

            // 将临时文件夹下第一层文件记录的父文件夹ID更新为目标文件夹ID
            List<File> copyAllList = fileMapper.selectFilesByUserIDAndParentID(user.getId(), temporary.getId());
            for (File file : copyAllList) {
                file.setParentId(parentId);
                int result = fileMapper.updateByPrimaryKeySelective(file);
                if (result <= 0) {
                    fileServiceCommon.removeOSSFileBatch(copyFileList);
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return R.createByErrorMessage("复制失败");
                }
            }

            // 更新容量，移除临时文件夹
            user.setUseCapacity(user.getUseCapacity() + sum);
            int resultUser = userMapper.updateByPrimaryKeySelective(user);
            int resultFile = fileMapper.deleteByPrimaryKey(temporary.getId());
            if (resultUser > 0 && resultFile > 0) {
                return R.createBySuccessMessage("复制成功");
            }

            // 回滚，并把已复制的文件持久记录移除
            fileServiceCommon.removeOSSFileBatch(copyFileList);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return R.createByErrorMessage("复制失败");
        }

        @Transactional(rollbackFor = Exception.class)
        public Long copy(Long id, Long parentId, Long userId, OSS ossClient, List<File> copyFileList) {
            File file = fileMapper.selectByPrimaryKey(id);
            long sizeCount = 0L;
            if (file == null) return sizeCount;
            long oldId = file.getId();
            file.setId(null);
            file.setParentId(parentId);
            file.setShareId(-1L);
            if (file.isAFolder()) {
                int result = fileMapper.insert(file);
                if (result > 0) {
                    List<File> files = fileMapper.selectFilesByUserIDAndParentID(userId, oldId);
                    sizeCount += files
                            .stream()
                            .mapToLong(innerFile -> copy(innerFile.getId(), file.getId(), userId, ossClient, copyFileList))
                            .sum();
                }
            } else {
                if (ossUtil.objectNameExists(ossClient, file.getRandomFileName())) {
                    String newName = ossUtil.getRandomFileName(file.getOldFileName());
                    String oldName = file.getRandomFileName();
                    file.setRandomFileName(newName);
                    int result = fileMapper.insert(file);
                    if (result > 0) {
                        copyFileList.add(file);
                        ossUtil.copy(ossClient, oldName, newName);
                        sizeCount += file.getFileSize();
                    }
                }
            }
            return sizeCount;
        }
    }

    @Component
    public static class FileServiceCommon {

        private final FileMapper fileMapper;
        private final ShareService shareService;
        private final ThreadPoolTaskExecutor taskExecutor;
        private final OSSUtil ossUtil;

        @Autowired
        public FileServiceCommon(FileMapper fileMapper, ThreadPoolTaskExecutor taskExecutor, ShareService shareService, OSSUtil ossUtil) {
            this.fileMapper = fileMapper;
            this.shareService = shareService;
            this.taskExecutor = taskExecutor;
            this.ossUtil = ossUtil;
        }

        private void removeOSSFileBatch(List<File> fileList) {
            if (fileList != null && !fileList.isEmpty()) {
                taskExecutor.execute(new OSSRemoveBatchRunnable(fileList, shareService, ossUtil));
            }
        }

        // 递归查询所在parentId所在路径
        private void findAbsolutePathByParentId(List<AbsolutePath> absolutePathList, Long parentId) {
            File parent = fileMapper.selectByPrimaryKey(parentId);
            if (parent != null && parent.getParentId() != -1) {
                AbsolutePath node = new AbsolutePath();
                node.setParentid(parent.getId());
                node.setFolderName(parent.getOldFileName());
                absolutePathList.add(node);
                File grandParent = fileMapper.selectByPrimaryKey(parent.getParentId());
                findAbsolutePathByParentId(absolutePathList, grandParent.getId());
            }
        }

        // 递归查询所有子节点包含自身(文件&文件夹)
        private void findChildByParentId(List<File> fileLinkedList, Long parentId, Long userId) {
            File file = fileMapper.selectByPrimaryKey(parentId);
            if (file != null) fileLinkedList.add(file);
            List<File> fileOfParentIdList = fileMapper.selectFilesByUserIDAndParentID(userId, parentId);
            fileOfParentIdList.forEach(innerFile -> findChildByParentId(fileLinkedList, innerFile.getId(), userId));
        }

        // 递归查询所有子节点文件大小包含自身
        private Long getParentIdSizeCount(Long parentId, Long userId) {
            File file = fileMapper.selectByPrimaryKey(parentId);
            long sizeCount = 0L;
            if (file == null) return sizeCount;
            if (file.isAFolder()) {
                List<File> files = fileMapper.selectFilesByUserIDAndParentID(userId, file.getId());
                sizeCount += files.stream().mapToLong(innerFile -> getParentIdSizeCount(innerFile.getId(), userId)).sum();
            } else {
                sizeCount += file.getFileSize();
            }
            return sizeCount;
        }
    }

    private static class OSSRemoveBatchRunnable implements Runnable {

        private final List<File> fileList;
        private final ShareService shareService;
        private final OSSUtil ossUtil;

        public OSSRemoveBatchRunnable(List<File> fileList, ShareService shareService, OSSUtil ossUtil) {
            this.fileList = fileList;
            this.shareService = shareService;
            this.ossUtil = ossUtil;
        }

        @Override
        public void run() {
            Long[] shareIds = this.fileList
                    .stream()
                    .filter(file -> !file.isAFolder() && file.getShareId() != null && file.getShareId() != -1)
                    .map(File::getShareId)
                    .toArray(Long[]::new);
            List<String> objectList = this.fileList
                    .stream()
                    .filter(file -> !file.isAFolder())
                    .map(File::getRandomFileName)
                    .collect(Collectors.toList());
            shareService.remove(shareIds);
            OSS ossClient = ossUtil.getOSSClient();
            ossUtil.deleteBatch(ossClient, objectList);
            ossClient.shutdown();
        }
    }
}